package tcp

import (
	"encoding/binary"
	"fisherman/common/logger"
	"fisherman/utils/crypte/sha256"
	"fisherman/utils/event"
	"fmt"
	"github.com/vmihailenco/msgpack/v5"
	"io"
	"net"
	"strings"
	"sync"
	"time"
)

type BaseMsg struct {
	Cmd    string
	FromId string
	ToId   string
	Data   []byte
}

type TcpServer struct {
	clientConns map[string]net.Conn
	mu          sync.Mutex // 用于保护 map 并发安全
	secret      string
}

func NewTcpServer(address string, secret string) {
	if strings.HasSuffix(address, ":0") {
		log.Error("[TcpServer]服务端启动失败: 传入端口为0")
		return
	}

	server := &TcpServer{clientConns: make(map[string]net.Conn), secret: secret}
	listener, err := net.Listen("tcp", address)
	if err != nil {
		log.Errorf("[TcpServer]服务端启动失败: %v", err)
		return
	}
	defer func(listener net.Listener) {
		err := listener.Close()
		if err != nil {
		}
	}(listener)
	log.Infof("[TcpServer]服务端启动成功，监听 %s", address)
	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Errorf("[TcpServer]网络错误: %v", err)
			continue
		}
		go server.handleConnection(conn)
	}
}

// handleConnection 处理客户端连接
func (s *TcpServer) handleConnection(conn net.Conn) {
	log.Info2File("[TcpServer]有客户端连接")
	var heartbeatChan chan bool
	var clientId string
	var once sync.Once

	//正式循环接收消息
	for {
		// 读取数据长度（4 字节）
		head := make([]byte, 4)
		_, err := io.ReadFull(conn, head)
		if err != nil {
			//log.Errorf("[TcpServer]读取Header错误: %v", err)
			break
		}
		bodyLen := binary.BigEndian.Uint32(head)

		// 读取实际数据
		body := make([]byte, bodyLen)
		_, err = io.ReadFull(conn, body)
		if err != nil {
			//log.Errorf("[TcpServer]读取Body错误: %v", err)
			break
		}

		msg, err := parseBody(body, s.secret)
		if err != nil {
			//log.Errorf("[TcpServer]解析消息体到BaseMsg失败: %v", err)
			break
		}

		if msg.Cmd == "heartbeat" {
			heartbeatChan <- true
			continue
		}

		fromId := msg.FromId
		toId := msg.ToId

		if msg.Cmd == "set_client_id" {
			s.mu.Lock()
			_, exists := s.clientConns[fromId]
			s.mu.Unlock()
			if exists {
				tip := fmt.Sprintf("客户端ID（%s）已存在，服务端拒绝连接", fromId)
				log.Infof2File(tip)
				sendMsg(conn, BaseMsg{Cmd_Error, fromId, fromId, []byte(tip)}, s.secret)
				go func() {
					time.Sleep(time.Millisecond * 500)
					once.Do(func() {
						err := conn.Close()
						if err != nil {
							return
						}
					})
				}()
				return
			}
			//加入连接列表
			clientId = fromId
			log.Infof2File("[TcpServer]连接的客户端ID是：%s", clientId)
			event.Emit(event.Ev_SendMsg2Tg, fmt.Sprintf("[TcpServer]有客户端连接: %s", clientId))
			s.mu.Lock()
			s.clientConns[clientId] = conn
			s.mu.Unlock()
			//启动心跳监听
			heartbeatChan = s.startListenHeartbeat(conn, &once, clientId)
			//广播全网
			for _, clientConn := range s.allConns() {
				sendMsg(clientConn, BaseMsg{Cmd_OnSomeoneConnect, fromId, "all", nil}, s.secret)
			}
			continue
		}

		//不做任何处理，直接转发原始数据
		allBytes := append(head, body...)
		if toId == "" || toId == "all" {
			//发给所有人
			for _, clientConn := range s.allConns() {
				clientConn.Write(allBytes)
			}
		} else if toId == "other" {
			//发给其他人
			for _, clientConn := range s.allConns() {
				if clientConn != conn {
					clientConn.Write(allBytes)
				}
			}
		} else {
			//发给特定的人
			s.mu.Lock()
			toConn, exists := s.clientConns[toId]
			s.mu.Unlock()
			if exists {
				toConn.Write(allBytes)
			} else {
				sendMsg(conn, BaseMsg{"error", fromId, fromId, []byte(fmt.Sprintf("toId`%s`不存在", toId))}, s.secret)
			}
		}
	}

	if clientId != "" {
		// 连接断开后，从 map 中移除客户端
		s.mu.Lock()
		if _, exists := s.clientConns[clientId]; exists {
			delete(s.clientConns, clientId)
		}
		s.mu.Unlock()
		// 通知其他客户端该连接已断开
		for _, clientConn := range s.allConns() {
			sendMsg(clientConn, BaseMsg{Cmd_OnSomeoneDisconnect, clientId, "all", nil}, s.secret)
		}
		event.Emit(event.Ev_SendMsg2Tg, fmt.Sprintf("[TcpServer]有客户端断开: %s", clientId))
	} else {
		event.Emit(event.Ev_SendMsg2Tg, "[TcpServer]未知客户端已断开")
	}

	//log.Info("[TcpServer]关闭心跳监听")
	close(heartbeatChan)
	once.Do(func() {
		err := conn.Close()
		if err != nil {
			return
		}
	})
}

func (s *TcpServer) startListenHeartbeat(conn net.Conn, once *sync.Once, clientId string) (heartbeatChan chan bool) {
	//log.Info("[TcpServer]开始监听心跳")
	heartbeatChan = make(chan bool, 1)
	go func() {
		// 30秒心跳检查一次
		timeout := 15 * time.Second
		timer := time.NewTicker(timeout)
		defer timer.Stop()
		for {
			select {
			case <-timer.C:
				s.mu.Lock()
				if _, exists := s.clientConns[clientId]; exists {
					delete(s.clientConns, clientId)
				}
				s.mu.Unlock()
				log.Errorf("心跳超时，释放连接：%s", clientId)
				once.Do(func() {
					err := conn.Close()
					if err != nil {
						return
					}
				})
				return
			case _, isActive := <-heartbeatChan:
				if !isActive {
					//箭筒被主动关闭
					s.mu.Lock()
					delete(s.clientConns, clientId)
					s.mu.Unlock()
					once.Do(func() {
						err := conn.Close()
						if err != nil {
							return
						}
					})
					return
				}
				// 收到心跳信号，重置计时器
				timer.Stop()
				// 清理残留信号
				select {
				case <-timer.C:
				default:
				}
				timer.Reset(timeout)
				//log.Infof("收到心跳包，%d秒后继续检查...", timeout/1e9)
			}
		}
	}()
	return
}

// allConns 获取当前所有连接（防止直接操作 map）
func (s *TcpServer) allConns() []net.Conn {
	s.mu.Lock()
	defer s.mu.Unlock()
	conns := make([]net.Conn, 0, len(s.clientConns))
	for _, conn := range s.clientConns {
		conns = append(conns, conn)
	}
	return conns
}

func receiveMsg(conn net.Conn, secret string) (msg BaseMsg, err error) {
	// 读取数据长度
	head := make([]byte, 4)
	_, err = io.ReadFull(conn, head)
	if err != nil {
		return
	}
	bodyLen := binary.BigEndian.Uint32(head)

	// 读取实际数据
	body := make([]byte, bodyLen)
	_, err = io.ReadFull(conn, body)

	return parseBody(body, secret)
}

func parseBody(body []byte, secret string) (msg BaseMsg, err error) {
	var data []byte
	if secret != "" {
		//拆分出实际数据和末尾的签名hash值
		dataLen := len(body) - 32
		data = body[:dataLen]
		hash := body[dataLen:]
		if !sha256.VerifyBySha256(data, hash, secret) {
			err = fmt.Errorf("[TcpClient]验证签名失败")
			return
		}
	} else {
		data = body
	}
	err = msgpack.Unmarshal(data, &msg)
	return
}

func sendMsg(conn net.Conn, msg BaseMsg, secret string) {
	data, _ := msgpack.Marshal(msg)
	dataLen := len(data)
	//预计总长度，后用copy写入避免多次分配内存,最高效
	totalLen := 4 + dataLen
	if secret != "" {
		totalLen += 32
	}
	totalBytes := make([]byte, totalLen)
	//写入头部（一个记录消息体长度的整型数字）
	binary.BigEndian.PutUint32(totalBytes[:4], uint32(totalLen-4))
	//写入消息体
	if secret != "" {
		//带签名的消息体
		signedData := sha256.SignWithSha256(data, secret)
		copy(totalBytes[4:], signedData)
	} else {
		//无签名的消息体
		copy(totalBytes[4:], data)
	}
	//发送
	conn.Write(totalBytes)
}
